// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 包crc32实现32位循环冗余校验，或CRC-32，
// 校验和。看见https:
// 信息。
// 
// 多项式以LSB第一形式表示，也称为反向表示。
// 参见https:
// 了解更多信息。
package crc32

import (
	"errors"
	"hash"
	"sync"
	"sync/atomic"
)

// CRC-32校验和的大小，以字节为单位。
const Size = 4

// 预定义多项式。
const (
	// IEEE是迄今为止最常见的CRC-32多项式。
	// 用于以太网（IEEE 802.3）、v.42、fddi、gzip、zip、png等。。。
	IEEE = 0xedb88320

	// Castagnoli多项式，用于iSCSI。
	// 比IEEE具有更好的错误检测特性。
	// https:
	Castagnoli = 0x82f63b78

	// Koopman多项式。
	// 也比IEEE具有更好的错误检测特性。
	// https:
	Koopman = 0xeb31d82e
)

// Table是一个256字的表，表示有效处理的多项式。
type Table [256]uint32

// 此文件使用特定于体系结构的文件中实现的功能。
// 他们实现的接口如下：
// /
// /archAvailableIEEE报告特定于体系结构的CRC32-IEEE 
// /算法是否可用。
// archAvailableIEEE（）bool 
// 
// /archInitIEEE初始化特定于体系结构的CRC3-IEEE算法。
// /只有在archAvailableIEEE（）返回true时才能调用它。
// archInitIEEE（）
// 
// /archUpdateIEEE更新给定的CRC32-IEEE。只有在之前调用过
// /archInitIEEE（）时，才能调用它。
// archUpdateIEEE（crc uint32，p[]字节）uint32 
// 
// /archAvailableCastagnoli报告特定于体系结构的
// /CRC32-C算法是否可用。
// archAvailableCastagnoli（）bool 
// 
// /archInitCastagnoli初始化特定于体系结构的CRC32-C 
// /算法。只有当archAvailableCastagnoli（）返回
// /true时，才能调用它。
// archInitCastagnoli（）
// 
// /archUpdateCastagnoli更新给定的CRC32-C。只有在之前调用archInitCastagnoli（）时，它才能被称为
// 。
// archUpdateCastagnoli（crc uint32，p[]字节）uint32 

// castagnoliTable指向Castagnoli 
// 多项式的延迟初始化表。当被要求生成
// Castagnoli表时，MakeTable将始终返回此值，这样我们就可以对比它，找到调用方何时是
// 使用此多项式。
var castagnoliTable *Table
var castagnoliTable8 *slicing8Table
var castagnoliArchImpl bool
var updateCastagnoli func(crc uint32, p []byte) uint32
var castagnoliOnce sync.Once
var haveCastagnoli uint32

func castagnoliInit() {
	castagnoliTable = simpleMakeTable(Castagnoli)
	castagnoliArchImpl = archAvailableCastagnoli()

	if castagnoliArchImpl {
		archInitCastagnoli()
		updateCastagnoli = archUpdateCastagnoli
	} else {
		// 初始化8倍切片表。
		castagnoliTable8 = slicingMakeTable(Castagnoli)
		updateCastagnoli = func(crc uint32, p []byte) uint32 {
			return slicingUpdate(crc, castagnoliTable8, p)
		}
	}

	atomic.StoreUint32(&haveCastagnoli, 1)
}

// IEEETable是IEEE多项式的表格。
var IEEETable = simpleMakeTable(IEEE)

// ieeeTable8是IEEE的切片表
var ieeeTable8 *slicing8Table
var ieeeArchImpl bool
var updateIEEE func(crc uint32, p []byte) uint32
var ieeeOnce sync.Once

func ieeeInit() {
	ieeeArchImpl = archAvailableIEEE()

	if ieeeArchImpl {
		archInitIEEE()
		updateIEEE = archUpdateIEEE
	} else {
		// 初始化切片表。
		ieeeTable8 = slicingMakeTable(IEEE)
		updateIEEE = func(crc uint32, p []byte) uint32 {
			return slicingUpdate(crc, ieeeTable8, p)
		}
	}
}

// MakeTable返回由指定多项式构造的表。
// 此表内容不得修改。
func MakeTable(poly uint32) *Table {
	switch poly {
	case IEEE:
		ieeeOnce.Do(ieeeInit)
		return IEEETable
	case Castagnoli:
		castagnoliOnce.Do(castagnoliInit)
		return castagnoliTable
	}
	return simpleMakeTable(poly)
}

// 摘要表示校验和的部分计算。
type digest struct {
	crc uint32
	tab *Table
}

// New创建一个新的哈希。Hash32使用表中表示的
// 多项式计算CRC-32校验和。它的求和方法将
// 值按大端字节顺序排列。返回的Hash32还
// 实现编码。二进制封送和编码。BinaryUnmarshaler to 
// 对哈希的内部状态进行封送和解封。
func New(tab *Table) hash.Hash32 {
	if tab == IEEETable {
		ieeeOnce.Do(ieeeInit)
	}
	return &digest{0, tab}
}

// NewIEEE创建一个新的哈希。Hash32使用
// IEEE多项式计算CRC-32校验和。它的求和方法将以
// 大端字节顺序排列值。返回的Hash32还实现了
// 编码。二进制封送和编码。BinaryUnmarshaler封送
// 并对哈希的内部状态进行解组。
func NewIEEE() hash.Hash32 { return New(IEEETable) }

func (d *digest) Size() int { return Size }

func (d *digest) BlockSize() int { return 1 }

func (d *digest) Reset() { d.crc = 0 }

const (
	magic         = "crc\x01"
	marshaledSize = len(magic) + 4 + 4
)

func (d *digest) MarshalBinary() ([]byte, error) {
	b := make([]byte, 0, marshaledSize)
	b = append(b, magic...)
	b = appendUint32(b, tableSum(d.tab))
	b = appendUint32(b, d.crc)
	return b, nil
}

func (d *digest) UnmarshalBinary(b []byte) error {
	if len(b) < len(magic) || string(b[:len(magic)]) != magic {
		return errors.New("hash/crc32: invalid hash state identifier")
	}
	if len(b) != marshaledSize {
		return errors.New("hash/crc32: invalid hash state size")
	}
	if tableSum(d.tab) != readUint32(b[4:]) {
		return errors.New("hash/crc32: tables do not match")
	}
	d.crc = readUint32(b[8:])
	return nil
}

func appendUint32(b []byte, x uint32) []byte {
	a := [4]byte{
		byte(x >> 24),
		byte(x >> 16),
		byte(x >> 8),
		byte(x),
	}
	return append(b, a[:]...)
}

func readUint32(b []byte) uint32 {
	_ = b[3]
	return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
}

// Update返回将p中的字节添加到crc的结果。
func Update(crc uint32, tab *Table, p []byte) uint32 {
	switch {
	case atomic.LoadUint32(&haveCastagnoli) != 0 && tab == castagnoliTable:
		return updateCastagnoli(crc, p)
	case tab == IEEETable:
		// 不幸的是，由于IEEETable是导出的，所以在使用IEEE时可能没有对MakeTable的
		// 调用。我们必须确保它在这种情况下得到初始化。
		ieeeOnce.Do(ieeeInit)
		return updateIEEE(crc, p)
	default:
		return simpleUpdate(crc, tab, p)
	}
}

func (d *digest) Write(p []byte) (n int, err error) {
	switch {
	case atomic.LoadUint32(&haveCastagnoli) != 0 && d.tab == castagnoliTable:
		d.crc = updateCastagnoli(d.crc, p)
	case d.tab == IEEETable:
		// 我们只通过处理
		// 本例中的初始化。
		d.crc = updateIEEE(d.crc, p)
	default:
		d.crc = simpleUpdate(d.crc, d.tab, p)
	}
	return len(p), nil
}

func (d *digest) Sum32() uint32 { return d.crc }

func (d *digest) Sum(in []byte) []byte {
	s := d.Sum32()
	return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
}

// 校验和返回数据的CRC-32校验和
// 使用表中表示的多项式。
func Checksum(data []byte, tab *Table) uint32 { return Update(0, tab, data) }

// ChecksumIEEE使用IEEE多项式返回数据的CRC-32校验和
// 。
func ChecksumIEEE(data []byte) uint32 {
	ieeeOnce.Do(ieeeInit)
	return updateIEEE(0, data)
}

// tableSum返回表t的IEEE校验和。
func tableSum(t *Table) uint32 {
	var a [1024]byte
	b := a[:0]
	if t != nil {
		for _, x := range t {
			b = appendUint32(b, x)
		}
	}
	return ChecksumIEEE(b)
}
